#!/usr/bin/env node

import {Command} from 'commander';
import process from 'process';
import {createLogger} from '../../utils/Logger.js';
import {opsClient} from '../../utils/OpsClient.js';
import {fetchUserOpsToken} from '../../utils/OpsUser.js';
import {getGlobalOpsFile, getProjectOpsFile, initGlobalProjectOpsFile} from '../../utils/OpsFile.js';

/**
 * NB 命令处理上下文与公共辅助方法
 */
export class NbCommandHandler {

    constructor() {
        this.program = new Command();
        this.logger = createLogger('NB');
        this.setupCommands();
        this.setupSignalHandlers();
        this.globalOpsFile = getGlobalOpsFile();
        // 全局的项目缓存
        this.projectCacheFile = initGlobalProjectOpsFile();
        // 当前账户
        this.opsUser = this.globalOpsFile.read(('ops_user'));
        //上下文
        this.buildEnvMap = {
            'ft': '发布单隔离环境(feature-tag)',
            'feature': '稳定环境(feature)',
            'u': '稳定环境(uat/daily)',
            'daily': '稳定环境(uat/daily)',
            'uat': '稳定环境(uat/daily)'
        };
        this.pluginCommitCache = new Map();
        this.permissionDeniedRepos = new Map();
        this.reuseRepoPathCache = new Map();
        this.reuseExistingRepoEnabled = null;
        this.cachedInformMember = null;
        this.pendingIdeaOpenPath = null;
        this.cachedFeishuNoticeSwitches = null;
    }

    resetFeishuNoticeSwitches() {
        this.cachedFeishuNoticeSwitches = null;
    }

    assignProjectOpsFile(opsFile) {
        this.projectOpsFile = opsFile;
        this.resetFeishuNoticeSwitches();
        return this.projectOpsFile;
    }

    async initClient(verbose) {
        this.token = await fetchUserOpsToken(this.opsUser);
        // 当前用户的请求
        this.opsClient = opsClient(this.token, verbose);
    }

    /**
     * 设置命令和参数
     */
    setupCommands() {
        this.program
            .name('nb')
            .description('[N]ew [B]uild - 动动手指构建发布单、实时了解构建状态、自动走上线流程，体验nb的发布单！')
            .enablePositionalOptions()
            .version('1.0.0');

        // 初始化发布单
        this.program
            .command('init <target> [extras...]')
            .description('初始化发布单、依赖，任意位置执行，生成到默认目录下')
            .option('--fast', '使用浅克隆参数 (--single-branch --depth=1 --filter=blob:none) 加速下载', false)
            .option('--f', '同 --fast，便于与原有选项区分', false)
            .option('--http', '将仓库地址转换为 HTTP 协议后再执行 clone', false)
            .action((target, extras, options) => {
                const restArgs = Array.isArray(extras) ? extras : [];
                const opts = options && typeof options === 'object' ? options : {};
                this.handleInitCommand(target, restArgs, opts);
            });
        this.program
            .command('r')
            .description('刷新发布单发布单、依赖，任意位置执行，生成到默认目录下')
            .action(() => {
                this.handleRefreshCommand();
            });
        this.program
            .command('cr')
            .description('CR发布单中的项目')
            .action(() => {
                this.handleGitLabCr();
            });

        // 删除发布单
        this.program
            .command('del')
            .description('删除当前发布单的所有项目，上线之后可以执行')
            .action(() => {
                this.handleDeleteCommand();
            });

        // 显示项目信息 - 支持环境参数
        this.program
            .command('info')
            .description('显示当前项目信息')
            .option('-f, --feature [appNames...]', '显示feature环境信息')
            .option('-t, --ft [appNames...]', '显示feature-tag环境信息')
            .option('-u, --uat [appNames...]', '显示UAT环境信息')
            // .option('-b, --beta [appNames...]', '显示Beta环境信息')
            // .option('-p, --prod [appNames...]', '显示Production环境信息')
            .action((options) => {
                this.handleInfoCommand(options);
            });

        // 跳转到发布单网页
        this.program
            .command('jump')
            .option('-s, --support', '跳转依赖发布单')
            .option('-p, --plan', '跳转发布计划')
            .option('-o, --ops', '发布单')
            .description('跳转打开发布单网页')
            .action((options) => {
                this.handleJumpCommand(options).catch(() => {
                    process.exitCode = 1;
                });
            });
        this.program
            .command('log')
            .description('查询 EasySearch 链路日志，直接在控制台查看')
            .argument('<query>', 'Lucene 查询语句，例如 index:\"buzz-service\" AND reqId:\"xxxxxx\"')
            .argument('[lookback]', '回溯时长，支持格式 15m、2h、1d，默认 15m')
            .option('-s, --size <size>', '返回日志条数，默认 50')
            .option('--raw', '输出原始 JSON 结果', false)
            .option('-f, --follow', '持续监听匹配日志，类似 tail -f', false)
            .option('--verbose', '打印调试信息', false)
            .action((query, lookback, options) => {
                this.handleLogCommand(query, lookback, options).catch(error => {
                    this.logger.error(`日志查询失败: ${error.message}`);
                    if (error.stack) {
                        this.logger.debug?.(error.stack);
                    }
                    process.exitCode = 1;
                });
            });

        // 跳转到发布单网页
        this.program
            .command('go')
            .allowExcessArguments(false)
            .description('自动执行 NB GO 流程，串联构建、提测与发布计划步骤')
            .action(() => {
                this.handleGoCommand().catch(() => {
                    process.exitCode = 1;
                });
            });
        this.program
            .command('clone <target>')
            .description('克隆应用或别名对应的仓库')
            .option('--verbose', '详细输出', false)
            .option('--fast', '使用浅克隆参数 (--single-branch --depth=1 --filter=blob:none) 加速下载', false)
            .option('--f', '同 --fast，兼容快捷输入', false)
            .option('--http', '将仓库地址转换为 HTTP 协议后再执行 clone', false)
            .action((target, options) => {
                this.handleCloneCommand(target, options).catch(error => {
                    this.logger.error(`克隆失败: ${error.message}`);
                    process.exitCode = 1;
                });
            });

        const mergeCommand = this.program
            .command('merge')
            .description('分支辅助工具');
        mergeCommand
            .command('to <target>')
            .description('根据目标发布单生成自动合并配置')
            .action((target) => {
                this.handleMergeToCommand(target).catch(error => {
                    this.logger.error(`处理失败: ${error.message}`);
                    process.exitCode = 1;
                });
            });

        this.program
            .option('-a, --all', '构建该项目内所有依赖和应用')
            .option('-s, --support [appNames...]', '构建所有依赖并发布，默认所有应用')
            .option('-t, --ft [appNames...]', '构建依赖发布单，默认所有应用')
            .option('-f, --feature [appNames...]', '将指定应用快速打包至Feature(or Stable)，默认打包所有应用')
            .option('-u, --uat [appNames...]', '将指定应用快速打包至Uat，默认打包所有应用')
            .option('--m', '使用master分支构建而不是当前分支')
            .option('--c', '使用当前分支，而不是发布单分支')
            .option('--test', '打包覆盖率')
            .option('--verbose', '详细输出', false)
            .arguments('[appNames...]')
            .action((apps, options, command) => {
                if (apps && apps.length > 0 && !options.feature) {
                    // 没有 -f 的情况下，把位置参数当 feature
                    options.ft = apps;
                }
                this.handleMainCommand(options, command).then(() => {
                });
            });

    }

    /**
     * 设置信号处理器
     */
    setupSignalHandlers() {
        process.on('SIGINT', () => {
            this.logger.warn('\\n检测到 Ctrl+C，正在终止所有构建任务...');
            this.logger.info('构建任务已终止');
            process.exit(0);
        });
    }

    /**
     * 确保 opsClient 已初始化
     * @param {boolean} verbose
     * @returns {Promise<void>}
     */
    async ensureOpsClient(verbose = false) {
        if (!this.opsClient) {
            await this.initClient(verbose);
            return;
        }
        if (verbose && !this.opsClient.verbose) {
            this.opsClient.verbose = true;
        }
    }

    /**
     * 解析并执行命令
     */
    async parseAndExecute(args) {
        try {
            // 如果没有参数或者是 --help，显示自定义帮助
            if (args.length === 0 || args.includes('--help')) {
                this.showCustomHelp();
                return;
            }

            // 处理特殊的构建命令格式 (nb 'app1,app2')
            if (args.length === 1 && !args[0].startsWith('-') && !['init', 'del', 'info', 'jump', 'r', 'cr'].includes(args[0])) {
                await this.ensureOpsClient(process.env.VERBOSE === 'true');
                this.assignProjectOpsFile(getProjectOpsFile());
                await this.handleFlowBuildCommand([args[0]]);
                return;
            }

            // 设置详细模式
            if (args.includes('--verbose')) {
                process.env.VERBOSE = 'true';
            }

            // 兼容 nb -u info 参数顺序：将前置环境参数移动到 info 后
            const normalizedArgs = this.normalizeInfoArgs(args);

            // 使用 commander 解析命令
            this.program.parse(['node', 'nb', ...normalizedArgs]);

        } catch (error) {
            this.logger.error('命令解析失败:', error.stack);
            if (process.env.VERBOSE) {
                this.logger.debug('详细错误:', error.stack);
            }
        }
    }

    normalizeInfoArgs(args = []) {
        const ENV_OPTIONS = new Set(['-u', '--uat', '-t', '--ft', '-f', '--feature']);
        const infoIndex = args.indexOf('info');
        if (infoIndex === -1) {
            return args;
        }

        const beforeInfo = args.slice(0, infoIndex);
        const afterInfo = args.slice(infoIndex + 1);

        const envOptions = [];
        const otherOptions = [];
        for (const token of beforeInfo) {
            if (ENV_OPTIONS.has(token)) {
                envOptions.push(token);
            } else {
                otherOptions.push(token);
            }
        }

        if (envOptions.length === 0) {
            return args;
        }

        // 将环境参数放到 info 后，其他参数保持在前面，后续参数保持原顺序
        return [...otherOptions, 'info', ...envOptions, ...afterInfo];
    }

}
